﻿using System.Collections.Generic;
using System.Collections;

public class GameCalcHelper {
	/// <summary>
	/// 计算结果
	/// </summary>
	public class CalcResult {
		public int type;
		public List<int> cards;
		public int maxCard;

		public CalcResult() {
			type = GameEnum.CardType.TYPE_NONE;
			cards = new List<int>();
			maxCard = 0;
		}

		public override string ToString ()
		{
			string toString = "Type: " + type + ", ";
			toString += "MaxCard: " + maxCard + ", ";
			toString += "Cards: " + cards + "";
			return toString;
		}
	}

	public static bool CanPlay(CalcResult cRes, CalcResult oRes) {
		if (cRes == null) {
			DebugInfo.Log ("GameCalcHelper.CanPlay: cRes is null");
			return false;
		}

		if (oRes.type == 0) {
			return true;
		}

		if ((cRes.type != oRes.type) && (cRes.type != GameEnum.CardType.TYPE_BOMB)) {
			DebugInfo.Log ("GameCalcHelper.CanPlay: type not ==");
			return false;
		}

		switch (cRes.type) {
		case GameEnum.CardType.TYPE_BOMB:
			if (oRes.type == GameEnum.CardType.TYPE_BOMB) {
				if ((oRes.maxCard % 100) == 2) {
					return (cRes.cards.Count >= oRes.cards.Count);
				}
				return ((cRes.maxCard % 100) > (oRes.maxCard % 100)) || (cRes.cards.Count > oRes.cards.Count);
			} else {
				return true;
			}
		case GameEnum.CardType.TYPE_ORDER_PAIR:
			if (cRes.cards.Count == oRes.cards.Count) {
				if (((cRes.cards[0] % 100) == ((oRes.cards[0] % 100) + 1)) || (cRes.cards[0] == 1)) {
					return true;
				}
			}
			break;
		case GameEnum.CardType.TYPE_ORDER:
			if (cRes.cards.Count == oRes.cards.Count) {
				if (((cRes.cards[0] % 100) == ((oRes.cards[0] % 100) + 1)) || (cRes.cards[0] == 1)) {
					return true;
				}
			}
			break;
		case GameEnum.CardType.TYPE_PAIR:
			return ((cRes.cards [0] % 100) == ((oRes.cards [0] % 100) + 1)) || (cRes.cards [0] == 1) || ((cRes.cards[0] % 100) == 2);
		case GameEnum.CardType.TYPE_SINGLE:
			return (cRes.cards [0] % 100) == ((oRes.cards [0] % 100) + 1) || ((cRes.cards[0] % 100) == 2);
		}

		return false;
	}

	public static CalcResult CalcCards(List<int> cards, int minCard = 0, int type = 0, int length = 0) {
		List<CalcResult> results = new List<CalcResult> ();

		DebugInfo.Error ("minCard: " + minCard + ", type: " + type + ", length: " + length);

		Hashtable cardMap = buildCardMap (cards);
		int numLaizi = 0;
		if (cardMap.ContainsKey (1)) {
			numLaizi = ((ArrayList)cardMap [1]).Count;
		}

		// 炸弹
		CalcResult bombResult = CheckBomb(cardMap, numLaizi, minCard, length, type == GameEnum.CardType.TYPE_BOMB);
		if (bombResult != null) {
			results.Add (bombResult);
		}

		// 连对
		if ((type == 0) || (type == GameEnum.CardType.TYPE_ORDER_PAIR)) {
			results.AddRange(CheckOrderPair(cardMap, numLaizi, minCard, length));
		}

		// 顺子
		if ((type == 0) || (type == GameEnum.CardType.TYPE_ORDER)) {
			results.AddRange(CheckOrder(cardMap, numLaizi, minCard, length));
		}

		// 对子
		if ((type == 0) || (type == GameEnum.CardType.TYPE_PAIR)) {
			CalcResult pairResult = CheckPair(cardMap, numLaizi, minCard);
			if (pairResult != null) {
				results.Add (pairResult);
			}
		}

		// 单张
		if ((type == 0) || (type == GameEnum.CardType.TYPE_SINGLE)) {
			CalcResult singleResult = CheckSingle(cardMap, numLaizi, minCard);
			if (singleResult != null) {
				results.Add (singleResult);
			}
		}

		// 计算出牌最多的出牌方式
		int nMaxCardNum = 0;
		int nCurType = 0;
		CalcResult endResult = null;
		foreach (CalcResult cRes in results) {
			DebugInfo.Error ("==> type: " + cRes.type + ", maxCard: " + cRes.maxCard);
			if ((cRes.cards.Count > nMaxCardNum) 
				|| ((cRes.cards.Count == nMaxCardNum) && (cRes.type > nCurType))) {
				nMaxCardNum = cRes.cards.Count;
				nCurType = cRes.type;
				endResult = cRes;
			}
		}
		return endResult;
	}

	static Hashtable buildCardMap(List<int> cards) {
		Hashtable cardMap = new Hashtable ();
		foreach (int card in cards) {
			int cardV = card % 100;

			ArrayList array = null;
			if (cardMap.ContainsKey (cardV)) {
				array = (ArrayList)cardMap[cardV];
			} else {
				array = new ArrayList ();
				cardMap [cardV] = array;
			}
			array.Add (card);
		}
		return cardMap;
	}

	/// <summary>
	/// 计算炸弹
	/// </summary>
	/// <returns>The bomb.</returns>
	/// <param name="cardMap">Card map.</param>
	/// <param name="nLaizi">N laizi.</param>
	static CalcResult CheckBomb(Hashtable cardMap, int nLaizi, int minCard = 0, int length = 0, bool isBomb = false) {
		CalcResult result = null;

		int bombCard = 0;
		int bombCardNum = 0;
		ArrayList bombCards = null;

		if (!isBomb) {
			foreach (int card in cardMap.Keys) {
				if (card == 1) {
					continue;
				}

				ArrayList cardCards = (ArrayList)cardMap [card];
				int cardNum = cardCards.Count + nLaizi;
				if ((cardNum >= 3) && (cardNum > bombCardNum) && (cardCards.Count > 0)) {
					bombCard = card;
					bombCardNum = cardNum;
					bombCards = cardCards;
					break;
				}
			}
		} else {
			foreach (int card in cardMap.Keys) {
				if (card == 1) {
					continue;
				}

				ArrayList cardCards = (ArrayList)cardMap [card];
				int cardNum = cardCards.Count + nLaizi;
				if ((cardNum >= 3) && (cardNum > bombCardNum) && (cardCards.Count > 0)) {
					if ((cardNum > length) 
						|| ((cardNum == length) && (card > minCard))
					) {
						bombCard = card;
						bombCardNum = cardNum;
						bombCards = cardCards;
						break;
					}
				}
			}
		}
			
		if (bombCard != 0) {
			result = new CalcResult ();
			result.type = GameEnum.CardType.TYPE_BOMB;
			foreach (int card in bombCards) {
				result.cards.Add (card);
				if (card > result.maxCard) {
					result.maxCard = card;
				}
			}
			if (result.cards.Count < bombCardNum) {
				int remaind = bombCardNum - result.cards.Count;
				for (int i = 0; i < remaind; ++i) {
					result.cards.Add (1);
				}
			}
		}
		return result;
	}

	/// <summary>
	/// 计算连对
	/// </summary>
	/// <returns>The order pair.</returns>
	/// <param name="cardMap">Card map.</param>
	/// <param name="nLaizi">N laizi.</param>
	static List<CalcResult> CheckOrderPair(Hashtable cardMap, int nLaizi, int minCard = 0, int length = 0) {
		List<CalcResult> results = new List<CalcResult>();

		if (minCard == 0) {
			foreach (int card in cardMap.Keys) {
				if (card == 1 || card == 2) {
					continue;
				}

				CalcResult result = CheckOrderPair2 (cardMap, nLaizi, card);
				if (result != null) {
					results.Add (result);
				}

				if ((nLaizi >= 2) && (card <= 12)) {
					result = CheckOrderPair3 (cardMap, nLaizi, card);
					if (result != null) {
						results.Add (result);
					}
				}
			}
		} else {
			CalcResult result = CheckOrderPair4 (cardMap, nLaizi, minCard, length);
			if (result != null) {
				results.Add (result);
			}
		}

		return results;
	}

	static CalcResult CheckOrderPair2(Hashtable cardMap, int nLaizi, int card) {
		CalcResult result = null;

		if (card == 14) { // A放在前面的时候作为1处理
			card = 1;
		}

		int maxCard = card;
		int usedLaizi = 0;
		int nSeq = 0;

		while (true) {
			if (cardMap.ContainsKey (maxCard)) {
				ArrayList cards = (ArrayList)cardMap [maxCard];
				if (cards.Count >= 2) {
					maxCard += 1;
					nSeq += 1;
				} else if ((cards.Count + nLaizi) >= 2) {
					maxCard += 1;
					nSeq += 1;
					usedLaizi += 1;

					nLaizi -= 1;
				} else {
					break;
				}
			} else if (nLaizi >= 2) {
				usedLaizi += 2;
				maxCard += 1;
				nSeq += 1;
				nLaizi -= 2;
			} else {
				break;
			}
		}

		if (nSeq >= 2) {
			result = new CalcResult ();
			result.type = GameEnum.CardType.TYPE_ORDER_PAIR;
			for (int cCard = card; cCard < maxCard; ++cCard) {
				if (cardMap.ContainsKey (cCard)) {
					if (cCard == 1) {
						cCard = 14;
					}

					ArrayList cards = (ArrayList)cardMap [cCard];
					if (cards.Count >= 2) {
						result.cards.Add ((int)(cards) [0]);
						result.cards.Add ((int)(cards) [1]);
					} else {
						result.cards.Add ((int)(cards) [0]);
						result.cards.Add (1);
					}
				} else {
					result.cards.Add (1);
					result.cards.Add (1);
				}
			}
			if (cardMap.ContainsKey (maxCard - 1)) {
				result.maxCard = (int)((ArrayList)cardMap [maxCard - 1]) [0];
			} else {
				result.maxCard = (maxCard - 1) + 100;
			}
		}

		return result;
	}

	static CalcResult CheckOrderPair3(Hashtable cardMap, int nLaizi, int card) {
		CalcResult result = null;

		int maxCard = card;
		int usedLaizi = 2;
		int nSeq = 1;

		nLaizi -= 2;

		while (true) {
			if (cardMap.ContainsKey (maxCard)) {
				ArrayList cards = (ArrayList)cardMap [maxCard];
				if (cards.Count >= 2) {
					maxCard += 1;
					nSeq += 1;
				} else if ((cards.Count + nLaizi) >= 2) {
					maxCard += 1;
					nSeq += 1;
					usedLaizi += 1;

					nLaizi -= 1;
				} else {
					break;
				}
			} else if (nLaizi >= 2) {
				usedLaizi += 2;
				maxCard += 1;
				nSeq += 1;
				nLaizi -= 2;
			} else {
				break;
			}
		}

		if (nSeq >= 2) {
			result = new CalcResult ();
			result.type = GameEnum.CardType.TYPE_ORDER_PAIR;
			result.cards.Add (1); result.cards.Add (1);
			for (int cCard = card; cCard < maxCard; ++cCard) {
				if (cardMap.ContainsKey (cCard)) {
					ArrayList cards = (ArrayList)cardMap [cCard];
					if (cards.Count >= 2) {
						result.cards.Add ((int)(cards) [0]);
						result.cards.Add ((int)(cards) [1]);
					} else {
						result.cards.Add ((int)(cards) [0]);
						result.cards.Add (1);
					}
				} else {
					result.cards.Add (1);
					result.cards.Add (1);
				}
			}
			if (cardMap.ContainsKey (maxCard - 1)) {
				result.maxCard = (int)((ArrayList)cardMap [maxCard - 1]) [0];
			} else {
				result.maxCard = (maxCard - 1) + 100;
			}
		}

		return result;
	}

	static CalcResult CheckOrderPair4(Hashtable cardMap, int nLaizi, int minCard, int length) {
		CalcResult result = null;

		int maxCard = minCard + 1;
		int usedLaizi = 0;
		int nSeq = 0;

		while (true) {
			if (cardMap.ContainsKey (maxCard)) {
				ArrayList cards = (ArrayList)cardMap [maxCard];
				if (cards.Count >= 2) {
					maxCard += 1;
					nSeq += 1;
				} else if ((cards.Count + nLaizi) >= 2) {
					maxCard += 1;
					nSeq += 1;
					usedLaizi += 1;

					nLaizi -= 1;
				} else {
					break;
				}
			} else if (nLaizi >= 2) {
				usedLaizi += 2;
				maxCard += 1;
				nSeq += 1;
				nLaizi -= 2;
			} else {
				break;
			}

			if (nSeq == length) {
				break;
			}
		}

		if (nSeq == length) {
			result = new CalcResult ();
			result.type = GameEnum.CardType.TYPE_ORDER_PAIR;
			for (int cCard = minCard + 1; cCard < maxCard; ++cCard) {
				if (cardMap.ContainsKey (cCard)) {
					ArrayList cards = (ArrayList)cardMap [cCard];
					if (cards.Count >= 2) {
						result.cards.Add ((int)(cards) [0]);
						result.cards.Add ((int)(cards) [1]);
					} else {
						result.cards.Add ((int)(cards) [0]);
						result.cards.Add (1);
					}
				} else {
					result.cards.Add (1);
					result.cards.Add (1);
				}
			}
			if (cardMap.ContainsKey (maxCard - 1)) {
				result.maxCard = (int)((ArrayList)cardMap [maxCard - 1]) [0];
			} else {
				result.maxCard = (maxCard - 1) + 100;
			}
		}

		return result;
	}

	/// <summary>
	/// 计算顺子
	/// </summary>
	/// <returns>The order pair.</returns>
	/// <param name="cardMap">Card map.</param>
	/// <param name="nLaizi">N laizi.</param>
	static List<CalcResult> CheckOrder(Hashtable cardMap, int nLaizi, int minCard = 0, int length = 0) {
		List<CalcResult> results = new List<CalcResult>();

		if (minCard == 0) {
			// 出牌
			foreach (int card in cardMap.Keys) {
				if (card == 1) {
					continue;
				}

				if (card <= 12 || card == 14) {
					CalcResult result = CheckOrder2 (cardMap, nLaizi, card);
					if (result != null) {
						results.Add (result);
					}
				}

				if ((nLaizi > 0) && (card <= 13)) {
					CalcResult result = CheckOrder3 (cardMap, nLaizi, card);
					if (result != null) {
						results.Add (result);
					}
				}
			}
		} else {
			CalcResult result = CheckOrder4 (cardMap, nLaizi, minCard, length);
			if (result != null) {
				results.Add (result);
			}
		}

		return results;
	}

	static CalcResult CheckOrder2(Hashtable cardMap, int nLaizi, int card) {
		CalcResult result = null;

		if (card == 14) { // A放在前面的时候作为1处理
			card = 1;
		}

		int maxCard = card + 1;
		int usedLaizi = 0;
		int nSeq = 0;

		// 出牌
		while (true) {
			if (cardMap.ContainsKey (maxCard)) {
				maxCard += 1;
				nSeq += 1;
			} else if (nLaizi > 0) {
				usedLaizi += 1;
				maxCard += 1;
				nSeq += 1;
				nLaizi -= 1;
			} else {
				break;
			}
		}

		if (nSeq >= 2) {
			result = new CalcResult ();
			result.type = GameEnum.CardType.TYPE_ORDER;
			for (int cCard = card; cCard < maxCard; ++cCard) {
				if (cCard == 1) {
					cCard = 14;
				}
				if (cardMap.ContainsKey (cCard)) {
					result.cards.Add ( (int)((ArrayList)cardMap [cCard]) [0]);
				} else {
					result.cards.Add (1);
				}
			}
			if (cardMap.ContainsKey (maxCard - 1)) {
				result.maxCard = (int)((ArrayList)cardMap [maxCard - 1]) [0];
			} else {
				result.maxCard = (maxCard - 1) + 100;
			}
		}

		return result;
	}

	static CalcResult CheckOrder3(Hashtable cardMap, int nLaizi, int card) {
		CalcResult result = null;

		int maxCard = card + 1;
		int usedLaizi = 1;
		int nSeq = 1;
		nLaizi -= 1;

		while (true) {
			if (cardMap.ContainsKey (maxCard)) {
				maxCard += 1;
				nSeq += 1;
			} else if (nLaizi > 0) {
				usedLaizi += 1;
				maxCard += 1;
				nSeq += 1;
				nLaizi -= 1;
			} else {
				break;
			}
		}

		if (nSeq >= 2) {
			result = new CalcResult ();
			result.type = GameEnum.CardType.TYPE_ORDER;
			result.cards.Add (1);
			for (int cCard = card; cCard < maxCard; ++cCard) {
				if (cardMap.ContainsKey (cCard)) {
					result.cards.Add ( (int)((ArrayList)cardMap [cCard]) [0]);
				} else {
					result.cards.Add (1);
				}
			}
			if (cardMap.ContainsKey (maxCard - 1)) {
				result.maxCard = (int)((ArrayList)cardMap [maxCard - 1]) [0];
			} else {
				result.maxCard = (maxCard - 1) + 100;
			}
		}

		return result;
	}

	static CalcResult CheckOrder4(Hashtable cardMap, int nLaizi, int minCard, int length) {
		CalcResult result = null;

		int maxCard = minCard + 1;
		int usedLaizi = 0;
		int nSeq = 0;

		while (true) {
			if (cardMap.ContainsKey (maxCard)) {
				maxCard += 1;
				nSeq += 1;
			} else if (nLaizi > 0) {
				usedLaizi += 1;
				maxCard += 1;
				nSeq += 1;
				nLaizi -= 1;
			} else {
				break;
			}

			if (nSeq == length) {
				break;
			}
		}

		if (nSeq == length) {
			result = new CalcResult ();
			result.type = GameEnum.CardType.TYPE_ORDER;
			for (int cCard = minCard + 1; cCard < maxCard; ++cCard) {
				if (cardMap.ContainsKey (cCard)) {
					result.cards.Add ( (int)((ArrayList)cardMap [cCard]) [0]);
				} else {
					result.cards.Add (1);
				}
			}
			if (cardMap.ContainsKey (maxCard - 1)) {
				result.maxCard = (int)((ArrayList)cardMap [maxCard - 1]) [0];
			} else {
				result.maxCard = (maxCard - 1) + 100;
			}
		}

		return result;
	}

	/// <summary>
	/// 计算对子(任意一对)
	/// </summary>
	/// <returns>The order pair.</returns>
	/// <param name="cardMap">Card map.</param>
	/// <param name="nLaizi">N laizi.</param>
	static CalcResult CheckPair(Hashtable cardMap, int nLaizi, int minCardV = 0) {
		CalcResult result = null;

		int minCard = int.MaxValue;
		int pairCard = 0;
		ArrayList pairCards = null;

		if (minCardV == 0) {
			// 出牌
			foreach (int card in cardMap.Keys) {
				if (card == 1) {
					continue;
				}

				ArrayList cardCards = (ArrayList)cardMap[card];
				int cardNum = nLaizi + cardCards.Count;
				if ((cardNum >= 2) 
					&& (cardCards.Count > 0)
					&& (card < minCard)
					&& (
						(card != 2)
						|| (
							(card == 2)
							&& (minCard == int.MaxValue)
						)
					)
				) {
					minCard = card;
					pairCard = card;
					pairCards = cardCards;
				}
			}
		} else {
			// 接牌
			if (minCard != 2) { // 不能接别人出得2
				int needCard = minCardV + 1;
				if (cardMap.ContainsKey (needCard)) {
					pairCards = (ArrayList)cardMap[needCard];
					if ((pairCards.Count + nLaizi) >= 2) {
						pairCard = needCard;
					}
				} else if (cardMap.ContainsKey(2)) { // 用2接
					pairCards = (ArrayList)cardMap[2];
					if ((pairCards.Count + nLaizi) >= 2) {
						pairCard = 2;
					}
				}
			}
		}


		if (pairCard != 0) {
			result = new CalcResult ();
			result.type = GameEnum.CardType.TYPE_PAIR;
			foreach (int card in pairCards) {
				result.cards.Add (card);
				if (card > result.maxCard) {
					result.maxCard = card;
				}
			}
			if ((result.cards.Count == 1) && (nLaizi > 0)) {
				result.cards.Add (1);
			}
		}

		return result;
	}

	/// <summary>
	/// 计算单张(出最小的单张)
	/// </summary>
	/// <returns>The order pair.</returns>
	/// <param name="cardMap">Card map.</param>
	/// <param name="nLaizi">N laizi.</param>
	static CalcResult CheckSingle(Hashtable cardMap, int nLaizi, int minCardV) {
		CalcResult result = null;

		int minCard = int.MaxValue;
		ArrayList singleCards = null;

		if (minCardV == 0) {
			// 出牌
			foreach (int card in cardMap.Keys) {
				if (card == 1) {
					continue; // 不能出万能牌
				}

				ArrayList cardCards = (ArrayList)cardMap [card];
				if ((card < minCard)
				    && (
				        (card != 2)
				        || (
				            (card == 2)
				            && (minCard == int.MaxValue)
				        )
				    )) {
					minCard = card;
					singleCards = cardCards;
				}
			}	
		} else {
			// 接牌
			if (minCardV != 2) { // 不能接别人出得2
				int needCard = minCardV + 1;
				if (cardMap.ContainsKey (needCard)) {
					singleCards = (ArrayList)cardMap[needCard]; // 用匹配的牌接
				} else if (cardMap.ContainsKey(2)) {
					singleCards = (ArrayList)cardMap[2]; // 用2接
				}
			}
		}

		if (singleCards != null) {
			result = new CalcResult ();
			result.type = GameEnum.CardType.TYPE_SINGLE;
			result.maxCard = (int)singleCards[0];
			result.cards.Add (result.maxCard);
		}

		return result;
	}
}
